﻿using System;
using System.Linq;
using System.Windows.Input;
using WiMo.Games.Inputs;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Controls;

namespace WiMo.Games.StandardInput
{
    public class InputManager : IInputManager
    {
        private Dictionary<Keys, KeyPressInfo> _keysDown = new Dictionary<Keys, KeyPressInfo>();
        private AutoResetEvent _keyAccess;
        private List<WiMo.Games.Inputs.TouchPoint> _localPoints = new List<WiMo.Games.Inputs.TouchPoint>();
        private Rectangle _leftSoftbuttonRectangle;
        private Rectangle _rightSoftbuttonRectangle;
        private Canvas _hostControl;
        private Location _clickPoint;

        public InputManager(Canvas control, IDisplay displayManager)
        {
            _hostControl = control;
            _hostControl.KeyDown += (object sender, KeyEventArgs e) => KeyDown((Keys)e.Key.GetWiMoKey());
            _hostControl.KeyUp += (object sender, KeyEventArgs e) => KeyUp((Keys)e.Key.GetWiMoKey());
            _hostControl.MouseLeftButtonDown += _hostControl_MouseLeftButtonDown;
            _hostControl.MouseLeftButtonUp += _hostControl_MouseLeftButtonUp;
            _hostControl.MouseMove += _hostControl_MouseMove;

            System.Windows.Input.Touch.FrameReported += new TouchFrameEventHandler(Touch_FrameReported);
          
            _keyAccess = new AutoResetEvent(true);

            DisplayManager = displayManager;

            MenuBarBackgroundColor = Color.Transparent;
            SoftMenuFontColor = Color.Black;

            _leftSoftbuttonRectangle = new Rectangle(0,
                (int)((displayManager.TargetSize.Height - 24)),
                (int)(displayManager.TargetSize.Width / 2),
                (int)(24 * displayManager.ScaleFactor.Y));

            _rightSoftbuttonRectangle = new Rectangle(_leftSoftbuttonRectangle.Width, _leftSoftbuttonRectangle.Top, _leftSoftbuttonRectangle.Width, _leftSoftbuttonRectangle.Height);

            TouchPoints = new List<WiMo.Games.Inputs.TouchPoint>();
        }

        void Touch_FrameReported(object sender, TouchFrameEventArgs e)
        {
            foreach(var point in e.GetTouchPoints(null))
            {
            	switch(point.Action)
                {
                    case TouchAction.Down:
                        var clickPoint = new WiMo.Games.Inputs.TouchPoint(point.TouchDevice.Id, (float)point.Position.X, (float)point.Position.Y, 0, WiMo.Games.Inputs.TouchPoint.TouchLocationState.Pressed);
                        _keyAccess.WaitOne();
                        _localPoints.Add(clickPoint);
                        _keyAccess.Set();
                        break;
                    case TouchAction.Move:
                        var movePoint = new WiMo.Games.Inputs.TouchPoint(point.TouchDevice.Id, (float)point.Position.X, (float)point.Position.Y, 0, WiMo.Games.Inputs.TouchPoint.TouchLocationState.Moved);
                        _keyAccess.WaitOne();
                        _localPoints.Add(movePoint);
                        _keyAccess.Set();
                        break;
                    case TouchAction.Up:
                        var upPoint = new WiMo.Games.Inputs.TouchPoint(point.TouchDevice.Id, (float)point.Position.X, (float)point.Position.Y, 0, WiMo.Games.Inputs.TouchPoint.TouchLocationState.Released);
                        _keyAccess.WaitOne();
                        _localPoints.Add(upPoint);
                        _keyAccess.Set();
                        break;
                }
            }
        }

        public void StartHopper()
        {
         	
        }

        public void StopHopper()
        {
            
        }

        void _hostControl_MouseMove(object sender, MouseEventArgs e)
        {
            var point = ScalePoint(e); 
            _clickPoint = new Location(point.X, point.Y);
            var touchPoint = new WiMo.Games.Inputs.TouchPoint(1, (float)point.X, (float)point.Y, 1.0f, WiMo.Games.Inputs.TouchPoint.TouchLocationState.Moved);

            _keyAccess.WaitOne();
            _localPoints.Add(touchPoint);
            _keyAccess.Set();
        }

        private Location ScalePoint(MouseEventArgs args)
        {
            var point = args.GetPosition(_hostControl);

            return new Location((point.X - DisplayManager.DisplayOffset.X) / DisplayManager.ScaleFactor.X, (point.Y - DisplayManager.DisplayOffset.Y) / DisplayManager.ScaleFactor.Y);
        }

        private Location ScalePoint(MouseButtonEventArgs args)
        {
            var point = args.GetPosition(_hostControl);

            return new Location((point.X - DisplayManager.DisplayOffset.X) / DisplayManager.ScaleFactor.X, (point.Y - DisplayManager.DisplayOffset.Y) / DisplayManager.ScaleFactor.Y);
        }


        void _hostControl_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("UP");
            var point = ScalePoint(e); 
            _clickPoint = Location.Empty;

            if (_keysDown.ContainsKey(Keys.F1))
                KeyUp(Keys.F1);

            if (_keysDown.ContainsKey(Keys.F2))
                KeyUp(Keys.F2);

            var touchPoint = new WiMo.Games.Inputs.TouchPoint(1, (float)point.X, (float)point.Y, 1.0f, WiMo.Games.Inputs.TouchPoint.TouchLocationState.Released);
            _keyAccess.WaitOne();
            _localPoints.Add(touchPoint);
            _keyAccess.Set();

            IsMouseUp = true;

            System.Diagnostics.Debug.WriteLine("Mouse Up-> " + touchPoint.ToString());
        }

        void _hostControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var point = ScalePoint(e); 
            _clickPoint = new Location(point.X, point.Y);

            if (_leftSoftbuttonRectangle.Contains(_clickPoint) && !string.IsNullOrEmpty(LeftSoftButton))
                KeyDown(Keys.F1);

            if (_rightSoftbuttonRectangle.Contains(_clickPoint) && !string.IsNullOrEmpty(RightSoftButton))
                KeyDown(Keys.F2);

            IsMouseUp = false;

            var touchPoint = new WiMo.Games.Inputs.TouchPoint(1, (float)point.X, (float)point.Y, 1.0f, WiMo.Games.Inputs.TouchPoint.TouchLocationState.Pressed);
            _keyAccess.WaitOne();
            _localPoints.Add(touchPoint);
            _keyAccess.Set();

            System.Diagnostics.Debug.WriteLine("Mouse Down-> " + touchPoint.ToString());
        }

        private InputManager() 
        {
            TouchPoints = new List<WiMo.Games.Inputs.TouchPoint>();
        }

        internal void KeyUp(Keys key)
        {
            if (key == Keys.Return || key == Keys.F1 || key == Keys.F2)
            {
                new Thread(() =>
                {
                    Thread.Sleep(200);
                    _keyAccess.WaitOne();
                    System.Diagnostics.Debug.WriteLine("UP -> " + key);
                    _keysDown.Remove(key);
                    _keyAccess.Set();
                }).Start();
            }
            else
            {
                _keyAccess.WaitOne();
                System.Diagnostics.Debug.WriteLine("UP -> " + key);
                _keysDown.Remove(key);
                _keyAccess.Set();
            }
        }

        internal void KeyDown(Keys key)
        {
            _keyAccess.WaitOne();
            if (!_keysDown.ContainsKey(key))
            {
                System.Diagnostics.Debug.WriteLine("DOWN -> " + key);
                _keysDown.Add(key, new KeyPressInfo(key));
                //_charBuffer.Add(key);
            }
            _keyAccess.Set();
        }

        public ICharacterBuffer CharacterBuffer { get; private set; }
        public IDisplay DisplayManager { get; set; }
        public Vector3 GVector { get; private set; }

        public System.Collections.Generic.List<WiMo.Games.Inputs.TouchPoint> TouchPoints { get; private set; }

        public bool UseAccelerometerAsThumbPad { get; set; }
        public Vector2 AccelerometerDPadThreshold { get; set; }
        public string LeftSoftButton { get; set; }
        public string RightSoftButton { get; set; }
        public Color SoftMenuFontColor { get; set; }
        public Color MenuBarBackgroundColor { get; set; }
        public ISpriteFont SoftMenuFont { get; set; }

        public bool WasPressed(IInputManager lastState, Keys key)
        {
            return (IsKeyDown(key) && lastState.IsKeyUp(key));
        }

        public bool IsPressed(IInputManager inputManager, Keys key, int debounceMs, bool fromAccelerometer)
        {
            KeyPressInfo keyInfo;
            if (_keysDown.TryGetValue(key, out keyInfo))
            {
                return keyInfo.Debounce(debounceMs) && keyInfo.FromAccelerometer == fromAccelerometer;
            }
            else
                return false;
        }

        public bool IsPressed(IInputManager inputManager, Keys key, int debounceMs)
        {
            KeyPressInfo keyInfo;
            if (_keysDown.TryGetValue(key, out keyInfo))
            {
                return keyInfo.Debounce(debounceMs);
            }
            else
                return false;
        }

        public bool IsKeyDown(Keys key)
        {
            return _keysDown.ContainsKey(key);
        }

        public bool IsKeyUp(Keys key)
        {
            return !_keysDown.ContainsKey(key);
        }

        public bool IsMouseUp { get; private set; }

        public bool WasScreenTouched(IInputManager lastInput)
        {
            if (lastInput == null)
                return false;

            return (!IsMouseUp && lastInput.IsMouseUp);
        }

        public bool WasScreenTouched(IInputManager lastInput, Rectangle rect)
        {
            if (lastInput == null)
                return false;

            return !IsMouseUp && lastInput.IsMouseUp && rect.Contains(_clickPoint);
        }        

        public IInputManager GetKeysState()
        {
            return this.Clone();
        }

        private IInputManager Clone()
        {
            var inputManager = new InputManager();
            _keyAccess.WaitOne();
            foreach (var key in _keysDown)
                inputManager._keysDown.Add(key.Key, new KeyPressInfo(key.Value.Key, key.Value.Start));

            inputManager.IsMouseUp = IsMouseUp;
            inputManager._clickPoint = _clickPoint;

            _keyAccess.Set();

            return inputManager;
        }

        public bool SupportAccelerometer
        {
            get { return false; }
        }

        public bool SupportMultiTouch
        {
            get { return true; }
        }

        public void Update()
        {
            TouchPoints = new List<WiMo.Games.Inputs.TouchPoint>();

            _keyAccess.WaitOne();

            foreach (var touchPoint in _localPoints)
                TouchPoints.Add(touchPoint);            

            _localPoints = new List<WiMo.Games.Inputs.TouchPoint>();

            _keyAccess.Set();
        }

        public void Draw(GameTime gameTime)
        {
            if (MenuBarBackgroundColor != Color.Transparent)
                DisplayManager.FillRectangle(new Rectangle(0, DisplayManager.TargetSize.Height - 20, DisplayManager.TargetSize.Width, 20), MenuBarBackgroundColor, MenuBarBackgroundColor, 1, WiMo.Games.Drawables.Origin.TopLeft);

            if (!string.IsNullOrEmpty(LeftSoftButton))
                DisplayManager.DrawText(new PointF(DisplayManager.TargetSize.Width / 4, DisplayManager.TargetSize.Height - 10), LeftSoftButton, SoftMenuFont, SoftMenuFontColor, WiMo.Games.Drawables.Origin.Center);

            if (!string.IsNullOrEmpty(RightSoftButton))
                DisplayManager.DrawText(new PointF((3 * DisplayManager.TargetSize.Width / 4), DisplayManager.TargetSize.Height - 10), RightSoftButton, SoftMenuFont, SoftMenuFontColor, WiMo.Games.Drawables.Origin.Center);            
        }
    }
}
